package genetic_algorithm;

import java.util.ArrayList;

/**
 * Creates a pattern for Chromosomes to follow and default functionality for the
 * Genetic Algorithm package.
 * @author pborrego
 */
public abstract class Chromosome {
    final static int FLIP = 0;
    final static int INSERT = 1;
    final static int DELETE = 2;
    private ArrayList<Gene<?>> genes;

    /**
     * Returns the fitness of the Chromosome as an interger value.
     * This should always be over written by the implementer.
     * @return -- an int value that represents the fitness of the Chromosome.
     */
    public int fitness() { return 0; }

    /**
     * Splits the Chromosome into n even genes chunks.
     * @param n -- number of Gene chunks to break the chromosome up into.
     * @return -- a Vector of the Gene chunks.
     */
    public ArrayList<Allele> splitChromosome(int n) {
        int size = this.genes.size();
        int base = Float.valueOf(size / n).intValue();
        int mod = size % n;
        int groupSize;
        int offset = 0;
        ArrayList<Allele> alleles = new ArrayList<Allele>();
        ArrayList<Gene<?>> tmp = new ArrayList<Gene<?>>();

        for(int i=0; i<n; i++) {
            groupSize = (mod > 0) ? base + 1 : base;

            for(int j=0; j<groupSize; j++) {
                tmp.add(this.genes.get(j+offset));
            }
            alleles.add(new Allele(tmp));
            tmp.clear();
            offset += groupSize;
        }
        return alleles;
    }

    /**
     * Randomly mutates one 1 Gene block of the Chromosome using the type.
     * FLIP -- Flips the value of the Gene block at random position.
     * INSERT -- Inserts a new Gene block at random position.
     * DELETE -- Deletes a Gene block at random position.
     * @param type -- FLIP, INSERT or DELETE value.
     */
    public void mutate(int type) {
        int pos = this.randomGene();

        switch(type) {
            case Chromosome.INSERT:
                this.genes.add(pos, this.genes.get(pos).clone());
                break;
            case Chromosome.DELETE:
                this.genes.remove(pos);
                break;
            case Chromosome.FLIP:
                this.genes.get(pos).flip();
                break;
        }
    }

    /**
     * Returns all the Gene blocks of this chromosome
     * @return -- Vector of all the Gene blocks.
     */
    public ArrayList<Gene<?>> getGenes() {
        return genes;
    }

    /**
     * Sets the Gene blocks of this chromosome.
     * @param genes -- A Vector of the new Gene blocks.
     */
    public void setGenes(ArrayList<Gene<?>> g) {
        this.genes = g;
    }

    /**
     * A utility to get a random Gene block from the chromosome.
     * @return -- Returns a random index position or -1 if there are no Gene blocks set.
     */
    protected int randomGene() {
        if(this.genes == null) return -1;

        return Double.valueOf(Math.random() * this.genes.size()).intValue();
    }

    /**
     * Returns a random mutation type. Remember to override this function if
     * more mutation types are added.
     * @return -- An integer representing a mutation type.
     */
    public static int randomMutationType() {
        return Double.valueOf(Math.random() * 3).intValue();
    }
}
